前言
如果从来不了解React先看前篇。
本文为了能将前篇学到的react知识学以致用,做了一个类似微博展示列表的demo。使用的是ES6+React+JSX+Webpack+Babel+NPM设计思路
图为截取的微博展示列表,我这么来划分组件:
花括号括起来的是我要写的几个组件:
- ContentImg组件:带图片的微博里的图片部分
- CommentForm组件:点击评论后,弹出来的评论下拉框
- OneWB组件:一条微博,这条微博可能是纯文字的,带一张图片的,带多张图片的,甚至是转发的。
- ListWB组件:OneWB的集合,是一个页面要展示的所有微博的集合。
最外层节点放在最上面,每个控件的数量通过后台给的数据控制,组件之间的关系用树状图表示:
本文章只实现纯文字,带图片的微博。带视屏,转发的可以用类似的方法实现,就不赘述了。后端数据的结构设计为:
var dataList=[ { headUrl:'http://img1.gtimg.com/tech/pics/hv1/238/85/1736/112905313.jpg', nickName:'Robin', content:'拿快递拿快递3号小邮局爆仓啦', NoCollect:132, NoForward:202, NoComment:142, NoPointGreat:423, contentImgUrls:[ "http://img1.gtimg.com/tech/pics/hv1/238/85/1736/112905313.jpg", "http://img1.gtimg.com/tech/pics/hv1/238/85/1736/112905313.jpg" ] }, { //内容同上 } ];
- 必填 headUrl:别人的头像链接,nickName:别人的昵称,content:微博文字内容,NoCollect:收藏数,NoForward:转发数,NoComment:评论数,NoPointGreat:点赞数。
- 非必填 contentImgUrls:微博图片内容,可以为空或不存在。
图中组件的数量是根据后端给的数据来决定的,dataList的元素个数代表OneWB的个数,contentImgUrls的元素个数决定了微博带不带图片,以及展示几个。
搭建环境
使用webpack+npm。
我的工程目录结构如图: webpack以commonjs的形式来书写脚本,是现在很火的模块加载器+打包工具。使用方法:1.创建package.json文件
npm init
2.在终端安装你需要的插件
npm install 插件名 --save-dev.
npm install -save-dev webpack
npm install -save-dev react
npm install -save-dev react-dom
npm install --save-dev babel-core
npm install --save-dev babel-loader
npm install --save-dev babel-preset-es2015
,babel6才需要装 npm install -save-dev babel-preset-react
,babel6才需要装 如果用的别人的工程,已存在package.json文件且内容完整,那么直接npm install不需要手动安装了。 3.创建webpack.config.js,并配置
根据webpack.config.js文件来决定webpack要做哪些动作。
//webpack.config.js 文件内容var path = require('path');module.exports = { entry: { 'index': './index.js' //key只是个名字,可以自由改 }, output: { path: './build', filename: 'entry.js',//也可以动态生成文件名 filename:'[name].js',将根据entry中的key生成名字 }, module: { loaders: [{ test: /\.jsx?$/, loader: 'babel', /* babel6 才需要配置这个,presets里面两个预编译插件,前一个用于编译es6,后一个用于编译react。按需配置。这个工程都需要。 query:{ presets: ['es2015','react'] }*/ }] }};
这段代码主要告诉了webpack:
- 哪个文件需要打包(entry字段),打包之后生成的新文件存到哪个路径(output中的path)、新文件叫什么名字(output中的filename);
- 要使用哪些加载器(module.loaders)。这里要使用babel来编译jsx和es6的代码。
总得来说,webpack从entry拿到目标文件,通过loaders进行编译,从output输出,其他功能由plugins引入。
注:index.js:负责渲染组件到页面上。相当于一个总的出口。因为会自动加载依赖关系,所以webpack.config.js文件只需要配置这一文件即可。 另外,这个工程比较简单,只需配置一个js文件。- 如果要打包多个js文件,这么配置:
entry: { 'file1': './index.js', //key只是个名字,可以自由改 'file2': './index2.js' }
- 如果需要打包css文件,或解析less文件,需要配置ExtractTextPlugin。详情看官方文档
4. 在终端执行命令:webpack
会在build文件夹下生成编译后(未压缩)的js文件,entry.js。如果编译错误会在命令行提示错误原因,entry.js为上一版本的内容。
你也可以使用其他命令,方便开发:webpack -w
监听你的代码修改,实施打包生成entry.js, 一般都直接使用这个命令。 webpack -p
打包js文件,并压缩。 5. 开始编码
完成以上四步,你就可以一边编写代码,用require(es6用import)来加载依赖的模块,一边在浏览器查看效果啦。并且如果以后有新的项目,直接拷贝package.json和webpack.config.js ,秒搭建环境。
编码阶段
需要用的react特性有:事件,state,props。依然可以参见上一篇的详细介绍。
- 每个组件文件都需要引入react
import React from 'react';
,以及依赖的其他组件。 index.js还需要引入reactdomimport ReactDOM from 'react-dom';
,因为渲染页面的方法render()是reactdom的。 - 用class定义组件,不要忘记使用
module.exports = ListWB;
开放组件给其他文件引用。
index.html
只需引入一个文件,编译后的文件entry.js。div#place为react组件的父容器。是不是看起来好像什么都没有呢?react中,通过js来插入组件,我们看index.js文件
weibo-react
index.js - js入口文件
使用自定义的react组件,渲染页面。
import React from 'react';import ReactDOM from 'react-dom';import ListWB from './components/list-wb.jsx';//微博列表组件var dataList=[{},{}];//这里存储的是后端传来的数据。包括头像,内容,点赞数等等ReactDOM.render(, document.getElementById('place'));
ListWB是微博列表组件,通过自定义属性data传递后端数据,并set到子组件中,展示到页面上。
注意import ListWB from './components/list-wb.jsx';
一定要记得引入自定义组件模块。 list-wb.jsx - 微博列表组件
import React from 'react';import OneWB from './one-wb.jsx';//标签的名字根据这个变量名来决定class ListWB extends React.Component{ render() { // 遍历后端给的数据,并且插入 var oneWBNodes = this.props.data.map(function(aWB,index){ return; }); return {oneWBNodes}; }}module.exports = ListWB;
笔记:
- map方法会遍历数组中的所有元素,并执行匿名函数,当前元素aWB作为匿名函数第一个实参,当前元素索引index作为第二个实参。this.props.data存储的后端数据list,每个元素执行完匿名函数return的值会连接(相加)起来。所以oneWBNodes得到是OneWB的list。
- 注意有一个自定义属性key。对于循环出的每个子组件,如果不定义key会报警。 react中data-reactid的取名方式: 左图为没有数组的“dom树”取名,右图为有数组的“dom树”取名。当图的.0.1是数组时,id会按照右图的虚线框那么取。 react组件中,每个标签都会有一个唯一的编号data-reactid,如:data-reactid=".0.$0a.0.1.0.1.1"。这里的key是data-reactid的最后一个小数点后面的值。所以key只需要在自己兄弟节点内唯一即可。 如果不按照要求,让多个子元素的key相同,那么只会识别展示第一个子元素。
one-wb.jsx - 单个微博组件
该组件代表单条微博。要处理的有:
- 属性 存储了后端传过来的数据。正确分配给子组件或者set到自己的原生属性中。
- 事件 触发后使用react的state来自动渲染页面。点击“评论”展开评论块,点击其他(如“转发”,“赞”)收起评论。这里通过给react的state增加一个字段isComment来控制评论区块的展现。点击“评论”,isComment设为true,展示评论区块;否则isComment设为false。
- JSX中的if-else JSX中是没有if-else的(因为jsx只是个语法糖,最终还是要翻译成原生js。比如:
<div id={if (condition) { 'msg' }}>Hello World!</div>
编译后是:
React.createElement("div", {id: if (condition) { 'msg' }}, "Hello World!");
然而我们还是有很多场景需要根据条件判断某些组件要不要展示。
我们用这几种方式来实现:- 三元操作符
React.render(Hello World!, mountNode);
- 在jsx以外赋值给变量
var vid=''; if(condition){ vid='msg'; } React.render(Hello World!, mountNode);
- 立即执行的匿名函数
{ if(this.state.isComment){ return 4; } })()}>
匿名函数这种方式也同样可以用在标签中间 <div>{(()=>{//函数内容})()}</div>
我使用的是第二种方式,代码还是更易维护一些。
这个文件的代码:import React from 'react';import CommentForm from './comment-form.jsx';import ContentImg from './content-img.jsx';class OneWB extends React.Component{ constructor(props){ super(props); this.state={ isComment:false, }; } render() { var oneData=this.props.oneData; var commentForm; var contentImgs; if(this.state.isComment) { //控制评论框是否展现,因为是动态的,所以放在state而不是props commentForm =; } if(oneData.contentImgUrls){ //若后端给的数据中有图片url,则展示 contentImgs = } return ; } handlerForwardClick(event) { if(event.target.innerText == '评论'){ this.setState({isComment:true}); }else{ this.setState({isComment:false}); } }}module.exports = OneWB;{commentForm}{oneData.nickName}{oneData.content}{contentImgs}
- 收藏 {oneData.NoCollect}
- 转发 {oneData.NoForward}
- 评论 {oneData.NoComment}
- 赞 {oneData.NoPointGreat}
content-img.jsx - 微博图片组件
因为子组件放在一个数组里,同样需要注意配置key
import React from 'react';class ContentImg extends React.Component{ render() { var imgNodes=this.props['content-img-urls'].map(function(oneImg,index){ return; }); return
- {imgNodes}
comment-form.jsx - 评论框组件
import React from 'react';class CommentForm extends React.Component{ render() { var imgUrl=this.props['data-my-head-img']; return; }}module.exports = CommentForm;
效果展示
总结
看完这篇文章,相信能够快速在工作当中实践了吧 ~(≧▽≦)/~ 。希望以后能出一些深刻的文章。